﻿using System;
using System.ComponentModel;
using System.Windows.Interactivity;
using nRoute.Behaviors.Interactivity;
using nRoute.Components;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;

namespace nRoute.Behaviors
{
    public class GoToStateAction
        : TargetedTriggerAction<FrameworkElement>
    {
        private const string NO_STATE_GROUP = "GoToStateAction's Target has no state groups";

        public static readonly DependencyProperty StateNameProperty =
            DependencyProperty.Register("StateName", typeof(string), typeof(GoToStateAction),
            new PropertyMetadata(Utilities.Compatability.DP_UNSET_VALUE));

        public static readonly DependencyProperty UseTransitionsProperty =
            DependencyProperty.Register("UseTransitions", typeof(bool), typeof(GoToStateAction),
            new PropertyMetadata(true));

        private FrameworkElement _stateTarget;

#region Properties

        [Category("Common Properties")]
        public string StateName
        {
            get { return Convert.ToString(GetValue(StateNameProperty)); }
            set
            {
                SetValue(StateNameProperty, value);
            }
        }

        [Category("Common Properties")]
        public bool UseTransitions
        {
            get { return Convert.ToBoolean(GetValue(UseTransitionsProperty)); }
            set { SetValue(UseTransitionsProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding StateNameBinding
        {
            get { return GetBinding(StateNameProperty); }
            set { SetBinding<string>(StateNameProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding UseTransitionsBinding
        {
            get { return GetBinding(UseTransitionsProperty); }
            set { SetBinding<bool>(UseTransitionsProperty, value); }
        }

#endregion

#region Overrides

        protected override void OnAttached()
        {
            base.OnAttached();
            if (_stateTarget == null) ResolveStatefulTarget();
        }

        protected override void OnTargetChanged(FrameworkElement oldTarget, FrameworkElement newTarget)
        {
 	        base.OnTargetChanged(oldTarget, newTarget);
            ResolveStatefulTarget();
        }

        protected override void Invoke(object parameter)
        {
            if (!string.IsNullOrWhiteSpace(this.StateName) && _stateTarget != null)
            {
                var _control = _stateTarget as Control;
                if (_control != null)
                {
                    _control.ApplyTemplate();
                    VisualStateManager.GoToState(_control, this.StateName, this.UseTransitions);
                }
                else
                {
                    ExtendedVisualStateManager.GoToElementState(_stateTarget, this.StateName, this.UseTransitions);
                }
            }
        }

#endregion

#region Helpers

        // based on logic from Microsoft.Expression.Interactivity.VisualStateUtilities

        private void ResolveStatefulTarget()
        {
            var _resolvedControl = default(FrameworkElement);
            if (string.IsNullOrEmpty(this.TargetName) && (this.Target == null))
            {
                if (!TryFindNearestStatefulControl(this.AssociatedObject , out _resolvedControl) && _resolvedControl != null)
                {
                    throw new InvalidOperationException(NO_STATE_GROUP);
                }
            }
            else
            {
                _resolvedControl = this.Target;
            }

            _stateTarget = _resolvedControl;
        }

        private bool TryFindNearestStatefulControl(FrameworkElement contextElement, out FrameworkElement resolvedControl)
        {
            var _targetElement = contextElement;
            if (_targetElement == null)
            {
                resolvedControl = default(FrameworkElement);
                return false;
            }
            else
            {
                var _parentElement = _targetElement.Parent as FrameworkElement;
                var _flag = true;

                // loop
                for (; !HasVisualStateGroupsDefined(_targetElement) && ShouldContinueTreeWalk(_parentElement); _parentElement = _parentElement.Parent as FrameworkElement)
                {
                    _targetElement = _parentElement;
                }

                if (HasVisualStateGroupsDefined(_targetElement))
                {
                    var _statefulElement = VisualTreeHelper.GetParent(_targetElement) as FrameworkElement;
                    if (_statefulElement != null && _statefulElement is Control)
                    {
                        _targetElement = _statefulElement;
                    }
                }
                else
                {
                    _flag = false;
                }

                resolvedControl = _targetElement;
                return _flag;
            }
        }

        private bool HasVisualStateGroupsDefined(FrameworkElement frameworkElement)
        {
            if (frameworkElement != null)
            {
                return VisualStateManager.GetVisualStateGroups(frameworkElement).Count != 0;
            }
            else
            {
                return false;
            }
        }

        private bool ShouldContinueTreeWalk(FrameworkElement element)
        {
            if (element == null || element is UserControl)
            {
                return false;
            }
            if (element.Parent == null)
            {
                var _templatedParent = FindTemplatedParent(element);
                if (_templatedParent == null || !(_templatedParent is Control) && !(_templatedParent is ContentPresenter))
                {
                    return false;
                }
            }
            return true;
        }

        private FrameworkElement FindTemplatedParent(FrameworkElement parent)
        {
            return VisualTreeHelper.GetParent(parent) as FrameworkElement;
        }

#endregion

    }
}
